#ifndef __OCPP_Cesium_Matrix3_H__
#define __OCPP_Cesium_Matrix3_H__

#include "Cartesian3.h"
#include "HeadingPitchRoll.h"

namespace OCPP
{
	namespace Cesium
	{
		class _CesiumExport Matrix3
		{
		public:
			std::vector<double> data = {
				0.0,0.0,0.0,
				0.0,0.0,0.0,
				0.0,0.0,0.0
			};

			Matrix3(DoubleVector dv)
			{
				this->data[0] = dv[0];
				this->data[1] = dv[1];
				this->data[2] = dv[2];
				this->data[3] = dv[3];
				this->data[4] = dv[4];
				this->data[5] = dv[5];
				this->data[6] = dv[6];
				this->data[7] = dv[7];
				this->data[8] = dv[8];
			}

			Matrix3(
				double column0Row0 = 0.0,
				double column1Row0 = 0.0,
				double column2Row0 = 0.0,
				double column0Row1 = 0.0,
				double column1Row1 = 0.0,
				double column2Row1 = 0.0,
				double column0Row2 = 0.0,
				double column1Row2 = 0.0,
				double column2Row2 = 0.0)
			{
				this->data[0] = column0Row0;
				this->data[1] = column0Row1;
				this->data[2] = column0Row2;
				this->data[3] = column1Row0;
				this->data[4] = column1Row1;
				this->data[5] = column1Row2;
				this->data[6] = column2Row0;
				this->data[7] = column2Row1;
				this->data[8] = column2Row2;
			}

			Matrix3& operator = (const Matrix3& fScaler)
			{
				data = fScaler.data;

				return (Matrix3&)*this;
			}

			double& operator[](size_t i)
			{
				return this->data[i];
			}

			int packedLength = 9;

			/**
			 * Stores the provided instance into the provided array1.
			 *
			 * @param {Matrix3} value The value to pack.
			 * @param {number[]} array1 The array1 to pack into.
			 * @param {number} [startingIndex=0] The index into the array1 at which to start packing the elements.
			 *
			 * @returns {number[]} The array1 that was packed into
			 */
			static DoubleVector pack(Matrix3& value, DoubleVector array1, size_t startingIndex = 0) {

				array1[startingIndex++] = value.data[0];
				array1[startingIndex++] = value.data[1];
				array1[startingIndex++] = value.data[2];
				array1[startingIndex++] = value.data[3];
				array1[startingIndex++] = value.data[4];
				array1[startingIndex++] = value.data[5];
				array1[startingIndex++] = value.data[6];
				array1[startingIndex++] = value.data[7];
				array1[startingIndex++] = value.data[8];

				return array1;
			};

			/**
			 * Retrieves an instance from a packed array1.
			 *
			 * @param {number[]} array1 The packed array1.
			 * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
			 * @param {Matrix3} [result] The object into which to store the result.
			 * @returns {Matrix3} The modified result parameter or a Matrix3 instance if one was not provided.
			 */
			static Matrix3 unpack(DoubleVector array1, size_t startingIndex = 0) {

				Matrix3 result;

				result.data[0] = array1[startingIndex++];
				result.data[1] = array1[startingIndex++];
				result.data[2] = array1[startingIndex++];
				result.data[3] = array1[startingIndex++];
				result.data[4] = array1[startingIndex++];
				result.data[5] = array1[startingIndex++];
				result.data[6] = array1[startingIndex++];
				result.data[7] = array1[startingIndex++];
				result.data[8] = array1[startingIndex++];
				return result;
			};

			/**
			 * Flattens an array1 of Matrix3s into an array1 of components. The components
			 * are stored in column-major order.
			 *
			 * @param {Matrix3[]} array1 The array1 of matrices to pack.
			 * @param {number[]} [result] The array1 onto which to store the result. If this is a typed array1, it must have array1.length * 9 components, else a {@link DeveloperError} will be thrown. If it is a regular array1, it will be resized to have (array1.length * 9) elements.
			 * @returns {number[]} The packed array1.
			 */
			static DoubleVector packArray(std::vector<Matrix3> array1) {
				auto length = array1.size();
				auto resultLength = length * 9;
				DoubleVector result;
				result.resize(resultLength);

				for (auto i = 0; i < length; ++i) {
					Matrix3::pack(array1[i], result, i * 9);
				}
				return result;
			};

			/**
			 * Unpacks an array1 of column-major matrix components into an array1 of Matrix3s.
			 *
			 * @param {number[]} array1 The array1 of components to unpack.
			 * @param {Matrix3[]} [result] The array1 onto which to store the result.
			 * @returns {Matrix3[]} The unpacked array1.
			 */
			static std::vector<Matrix3> unpackArray(DoubleVector array1) {
				auto length = array1.size();
				std::vector<Matrix3> result;
				result.resize(length / 9);

				for (auto i = 0; i < length; i += 9) {
					auto index = i / 9;
					result[index] = Matrix3::unpack(array1, i);
				}
				return result;
			};

			/**
			 * Creates a Matrix3 from 9 consecutive elements in an array1.
			 *
			 * @function
			 * @param {number[]} array1 The array1 whose 9 consecutive elements correspond to the positions of the matrix.  Assumes column-major order.
			 * @param {number} [startingIndex=0] The offset into the array1 of the first element, which corresponds to first column first row position in the matrix.
			 * @param {Matrix3} [result] The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Create the Matrix3:
			 * // [1.0, 2.0, 3.0]
			 * // [1.0, 2.0, 3.0]
			 * // [1.0, 2.0, 3.0]
			 *
			 * const v = [1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0];
			 * const m = Cesium.static Matrix3 fromArray(v);
			 *
			 * // Create same Matrix3 with using an offset into an array1
			 * const v2 = [0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0];
			 * const m2 = Cesium.static Matrix3 fromArray(v2, 2);
			 */
			static Matrix3 fromArray(DoubleVector array1, size_t startingIndex = 0) {
				return Matrix3::unpack(array1, startingIndex);
			};
			/**
			 * Creates a Matrix3 instance from a column-major order array1.
			 *
			 * @param {number[]} values The column-major order array1.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 */
			static Matrix3 fromColumnMajorArray(DoubleVector values) {
				return Matrix3(values);
			};

			/**
			 * Creates a Matrix3 instance from a row-major order array1.
			 * The resulting matrix will be in column-major order.
			 *
			 * @param {number[]} values The row-major order array1.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 */
			static Matrix3 fromRowMajorArray(DoubleVector values) {
				return Matrix3(
					values[0],
					values[1],
					values[2],
					values[3],
					values[4],
					values[5],
					values[6],
					values[7],
					values[8]
				);
			};

			/**
			 * Computes a 3x3 rotation matrix from the provided quaternion.
			 *
			 * @param {Quaternion} quaternion the quaternion to use.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The 3x3 rotation matrix from this quaternion.
			 */
			 static Matrix3 fromQuaternion(Quaternion quaternion);

			 /**
			  * Computes a 3x3 rotation matrix from the provided headingPitchRoll. (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles )
			  *
			  * @param {HeadingPitchRoll} headingPitchRoll the headingPitchRoll to use.
			  * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			  * @returns {Matrix3} The 3x3 rotation matrix from this headingPitchRoll.
			  */
			 static Matrix3 fromHeadingPitchRoll(HeadingPitchRoll headingPitchRoll) {
				 auto cosTheta = cos(-headingPitchRoll.pitch);
				 auto cosPsi = cos(-headingPitchRoll.heading);
				 auto cosPhi = cos(headingPitchRoll.roll);
				 auto sinTheta = sin(-headingPitchRoll.pitch);
				 auto sinPsi = sin(-headingPitchRoll.heading);
				 auto sinPhi = sin(headingPitchRoll.roll);

				 auto m00 = cosTheta * cosPsi;
				 auto m01 = -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi;
				 auto m02 = sinPhi * sinPsi + cosPhi * sinTheta * cosPsi;

				 auto m10 = cosTheta * sinPsi;
				 auto m11 = cosPhi * cosPsi + sinPhi * sinTheta * sinPsi;
				 auto m12 = -sinPhi * cosPsi + cosPhi * sinTheta * sinPsi;

				 auto m20 = -sinTheta;
				 auto m21 = sinPhi * cosTheta;
				 auto m22 = cosPhi * cosTheta;

				 return Matrix3(m00, m01, m02, m10, m11, m12, m20, m21, m22);
			 }

			  /**
			   * Computes a Matrix3 instance representing a non-uniform scale.
			   *
			   * @param {Cartesian3} scale The x, y, and z scale factors.
			   * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			   * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			   *
			   * @example
			   * // Creates
			   * //   [7.0, 0.0, 0.0]
			   * //   [0.0, 8.0, 0.0]
			   * //   [0.0, 0.0, 9.0]
			   * const m = Cesium.static Matrix3 fromScale(new Cesium.Cartesian3(7.0, 8.0, 9.0));
			   */
			static Matrix3 fromScale(Cartesian3 scale) {
				return Matrix3(scale.x, 0.0, 0.0, 0.0, scale.y, 0.0, 0.0, 0.0, scale.z);
			};

			/**
			 * Computes a Matrix3 instance representing a uniform scale.
			 *
			 * @param {number} scale The uniform scale factor.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Creates
			 * //   [2.0, 0.0, 0.0]
			 * //   [0.0, 2.0, 0.0]
			 * //   [0.0, 0.0, 2.0]
			 * const m = Cesium.static Matrix3 fromUniformScale(2.0);
			 */
			static Matrix3 fromUniformScale(double scale) {
				return Matrix3(scale, 0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0, scale);
			};

			/**
			 * Computes a Matrix3 instance representing the cross product equivalent matrix of a Cartesian3 vector.
			 *
			 * @param {Cartesian3} vector the vector on the left hand side of the cross product operation.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Creates
			 * //   [0.0, -9.0,  8.0]
			 * //   [9.0,  0.0, -7.0]
			 * //   [-8.0, 7.0,  0.0]
			 * const m = Cesium.static Matrix3 fromCrossProduct(new Cesium.Cartesian3(7.0, 8.0, 9.0));
			 */
			static Matrix3 fromCrossProduct(Cartesian3 vector) {
				return Matrix3(
					0.0,
					-vector.z,
					vector.y,
					vector.z,
					0.0,
					-vector.x,
					-vector.y,
					vector.x,
					0.0
				);
			};

			/**
			 * Creates a rotation matrix around the x-axis.
			 *
			 * @param {number} angle The angle, in radians, of the rotation.  Positive angles are counterclockwise.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Rotate a point 45 degrees counterclockwise around the x-axis.
			 * const p = new Cesium.Cartesian3(5, 6, 7);
			 * const m = Cesium.static Matrix3 fromRotationX(Cesium.Math.toRadians(45.0));
			 * const rotated = Cesium.Matrix3::multiplyByVector(m, p, new Cesium.Cartesian3());
			 */
			static Matrix3 fromRotationX(double angle) {
				auto cosAngle = cos(angle);
				auto sinAngle = sin(angle);

				return Matrix3(
					1.0,
					0.0,
					0.0,
					0.0,
					cosAngle,
					-sinAngle,
					0.0,
					sinAngle,
					cosAngle
				);
			};

			/**
			 * Creates a rotation matrix around the y-axis.
			 *
			 * @param {number} angle The angle, in radians, of the rotation.  Positive angles are counterclockwise.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Rotate a point 45 degrees counterclockwise around the y-axis.
			 * const p = new Cesium.Cartesian3(5, 6, 7);
			 * const m = Cesium.static Matrix3 fromRotationY(Cesium.Math.toRadians(45.0));
			 * const rotated = Cesium.Matrix3::multiplyByVector(m, p, new Cesium.Cartesian3());
			 */
			static Matrix3 fromRotationY(double angle) {
				auto cosAngle = cos(angle);
				auto sinAngle = sin(angle);

				return  Matrix3(
					cosAngle,
					0.0,
					sinAngle,
					0.0,
					1.0,
					0.0,
					-sinAngle,
					0.0,
					cosAngle
				);
			};

			/**
			 * Creates a rotation matrix around the z-axis.
			 *
			 * @param {number} angle The angle, in radians, of the rotation.  Positive angles are counterclockwise.
			 * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created.
			 * @returns {Matrix3} The modified result parameter, or a Matrix3 instance if one was not provided.
			 *
			 * @example
			 * // Rotate a point 45 degrees counterclockwise around the z-axis.
			 * const p = new Cesium.Cartesian3(5, 6, 7);
			 * const m = Cesium.static Matrix3 fromRotationZ(Cesium.Math.toRadians(45.0));
			 * const rotated = Cesium.Matrix3::multiplyByVector(m, p, new Cesium.Cartesian3());
			 */
			static Matrix3 fromRotationZ(double angle) {
				auto cosAngle = cos(angle);
				auto sinAngle = sin(angle);

				return  Matrix3(
					cosAngle,
					-sinAngle,
					0.0,
					sinAngle,
					cosAngle,
					0.0,
					0.0,
					0.0,
					1.0
				);
			};

			/**
			 * Creates an Array from the provided Matrix3 instance.
			 * The array1 will be in column-major order.
			 *
			 * @param {Matrix3} matrix The matrix to use..
			 * @param {number[]} [result] The Array onto which to store the result.
			 * @returns {number[]} The modified Array parameter or a new Array instance if one was not provided.
			 */
			DoubleVector toArray(Matrix3& matrix) {
				return DoubleVector{
					matrix.data[0],
					matrix.data[1],
					matrix.data[2],
					matrix.data[3],
					matrix.data[4],
					matrix.data[5],
					matrix.data[6],
					matrix.data[7],
					matrix.data[8],
				};
			};

			/**
			 * Computes the array1 index of the element at the provided row and column.
			 *
			 * @param {number} column The zero-based index of the column.
			 * @param {number} row The zero-based index of the row.
			 * @returns {number} The index of the element at the provided row and column.
			 *
			 * @exception {DeveloperError} row must be 0, 1, or 2.
			 * @exception {DeveloperError} column must be 0, 1, or 2.
			 *
			 * @example
			 * const myMatrix = new Cesium.Matrix3();
			 *  auto column1Row0Index = Cesium.Matrix3::getElementIndex(1, 0);
			 *  auto column1Row0 = myMatrix[column1Row0Index]
			 * myMatrix[column1Row0Index] = 10.0;
			 */
			static int getElementIndex(int column, int row) {
				return column * 3 + row;
			};

			/**
			 * Retrieves a copy of the matrix column at the provided index as a Cartesian3 instance.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {number} index The zero-based index of the column to retrieve.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 *
			 * @exception {DeveloperError} index must be 0, 1, or 2.
			 */
			static Cartesian3 getColumn(Matrix3& matrix, int index) {
				auto startIndex = index * 3;
				auto x = matrix.data[startIndex];
				auto y = matrix.data[startIndex + 1];
				auto z = matrix.data[startIndex + 2];
				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;
				return result;
			};

			/**
			 * Computes a new matrix that replaces the specified column in the provided matrix with the provided Cartesian3 instance.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {number} index The zero-based index of the column to set.
			 * @param {Cartesian3} cartesian The Cartesian whose values will be assigned to the specified column.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @exception {DeveloperError} index must be 0, 1, or 2.
			 */
			static Matrix3 setColumn(Matrix3& matrix, int index, Cartesian3 cartesian) {
				Matrix3 result = matrix;
				auto startIndex = index * 3;
				result.data[startIndex] = cartesian.x;
				result.data[startIndex + 1] = cartesian.y;
				result.data[startIndex + 2] = cartesian.z;
				return result;
			};

			/**
			 * Retrieves a copy of the matrix row at the provided index as a Cartesian3 instance.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {number} index The zero-based index of the row to retrieve.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 *
			 * @exception {DeveloperError} index must be 0, 1, or 2.
			 */
			static Cartesian3 getRow(Matrix3& matrix, int index) {
				auto x = matrix.data[index];
				auto y = matrix.data[index + 3];
				auto z = matrix.data[index + 6];
				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;
				return result;
			};

			/**
			 * Computes a new matrix that replaces the specified row in the provided matrix with the provided Cartesian3 instance.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {number} index The zero-based index of the row to set.
			 * @param {Cartesian3} cartesian The Cartesian whose values will be assigned to the specified row.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @exception {DeveloperError} index must be 0, 1, or 2.
			 */
			static Matrix3 setRow(Matrix3& matrix, int index, Cartesian3 cartesian) {
				Matrix3 result = matrix;
				result.data[index] = cartesian.x;
				result.data[index + 3] = cartesian.y;
				result.data[index + 6] = cartesian.z;
				return result;
			};

			/**
			 * Computes a new matrix that replaces the scale with the provided scale.
			 * This assumes the matrix is an affine transformation.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {Cartesian3} scale The scale that replaces the scale of the provided matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @see Matrix3::setUniformScale
			 * @see static Matrix3 fromScale
			 * @see static Matrix3 fromUniformScale
			 * @see Matrix3::multiplyByScale
			 * @see Matrix3::multiplyByUniformScale
			 * @see Matrix3::getScale
			 */
			static Matrix3 setScale(Matrix3& matrix, Cartesian3 scale) {
				auto existingScale = Matrix3::getScale(matrix);
				auto scaleRatioX = scale.x / existingScale.x;
				auto scaleRatioY = scale.y / existingScale.y;
				auto scaleRatioZ = scale.z / existingScale.z;
				Matrix3 result;
				result.data[0] = matrix.data[0] * scaleRatioX;
				result.data[1] = matrix.data[1] * scaleRatioX;
				result.data[2] = matrix.data[2] * scaleRatioX;
				result.data[3] = matrix.data[3] * scaleRatioY;
				result.data[4] = matrix.data[4] * scaleRatioY;
				result.data[5] = matrix.data[5] * scaleRatioY;
				result.data[6] = matrix.data[6] * scaleRatioZ;
				result.data[7] = matrix.data[7] * scaleRatioZ;
				result.data[8] = matrix.data[8] * scaleRatioZ;

				return result;
			};

			/**
			 * Computes a new matrix that replaces the scale with the provided uniform scale.
			 * This assumes the matrix is an affine transformation.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @param {number} scale The uniform scale that replaces the scale of the provided matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @see Matrix3::setScale
			 * @see static Matrix3 fromScale
			 * @see static Matrix3 fromUniformScale
			 * @see Matrix3::multiplyByScale
			 * @see Matrix3::multiplyByUniformScale
			 * @see Matrix3::getScale
			 */
			static Matrix3 setUniformScale(Matrix3& matrix, double scale) {
				Matrix3 result;
				auto existingScale = Matrix3::getScale(matrix);
				auto scaleRatioX = scale / existingScale.x;
				auto scaleRatioY = scale / existingScale.y;
				auto scaleRatioZ = scale / existingScale.z;

				result.data[0] = matrix.data[0] * scaleRatioX;
				result.data[1] = matrix.data[1] * scaleRatioX;
				result.data[2] = matrix.data[2] * scaleRatioX;
				result.data[3] = matrix.data[3] * scaleRatioY;
				result.data[4] = matrix.data[4] * scaleRatioY;
				result.data[5] = matrix.data[5] * scaleRatioY;
				result.data[6] = matrix.data[6] * scaleRatioZ;
				result.data[7] = matrix.data[7] * scaleRatioZ;
				result.data[8] = matrix.data[8] * scaleRatioZ;

				return result;
			};

			/**
			 * Extracts the non-uniform scale assuming the matrix is an affine transformation.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 *
			 * @see Matrix3::multiplyByScale
			 * @see Matrix3::multiplyByUniformScale
			 * @see static Matrix3 fromScale
			 * @see static Matrix3 fromUniformScale
			 * @see Matrix3::setScale
			 * @see Matrix3::setUniformScale
			 */
			static Cartesian3 getScale(Matrix3& matrix) {
				Cartesian3 result;
				result.x = Cartesian3::magnitude(
					Cartesian3::fromElements(matrix.data[0], matrix.data[1], matrix.data[2])
				);
				result.y = Cartesian3::magnitude(
					Cartesian3::fromElements(matrix.data[3], matrix.data[4], matrix.data[5])
				);
				result.z = Cartesian3::magnitude(
					Cartesian3::fromElements(matrix.data[6], matrix.data[7], matrix.data[8])
				);
				return result;
			};


			/**
			 * Computes the maximum scale assuming the matrix is an affine transformation.
			 * The maximum scale is the maximum length of the column vectors.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @returns {number} The maximum scale.
			 */
			static double getMaximumScale(Matrix3& matrix) {
				auto scaleScratch3 = Matrix3::getScale(matrix);
				return Cartesian3::maximumComponent(scaleScratch3);
			};

			/**
			 * Sets the rotation assuming the matrix is an affine transformation.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @param {Matrix3} rotation The rotation matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @see Matrix3::getRotation
			 */
			static Matrix3 setRotation(Matrix3& matrix, Matrix3 rotation) {
				auto scale = Matrix3::getScale(matrix);
				Matrix3 result;
				result[0] = rotation[0] * scale.x;
				result[1] = rotation[1] * scale.x;
				result[2] = rotation[2] * scale.x;
				result[3] = rotation[3] * scale.y;
				result[4] = rotation[4] * scale.y;
				result[5] = rotation[5] * scale.y;
				result[6] = rotation[6] * scale.z;
				result[7] = rotation[7] * scale.z;
				result[8] = rotation[8] * scale.z;

				return result;
			};

			/**
			 * Extracts the rotation matrix assuming the matrix is an affine transformation.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @see Matrix3::setRotation
			 */
			static Matrix3 getRotation(Matrix3& matrix) {
				auto scale = Matrix3::getScale(matrix);
				Matrix3 result;
				result.data[0] = matrix.data[0] / scale.x;
				result.data[1] = matrix.data[1] / scale.x;
				result.data[2] = matrix.data[2] / scale.x;
				result.data[3] = matrix.data[3] / scale.y;
				result.data[4] = matrix.data[4] / scale.y;
				result.data[5] = matrix.data[5] / scale.y;
				result.data[6] = matrix.data[6] / scale.z;
				result.data[7] = matrix.data[7] / scale.z;
				result.data[8] = matrix.data[8] / scale.z;

				return result;
			};

			/**
			 * Computes the product of two matrices.
			 *
			 * @param {Matrix3} left The first matrix.
			 * @param {Matrix3} right The second matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 multiply(Matrix3& left, Matrix3& right) {
				auto column0Row0 =
					left[0] * right[0] + left[3] * right[1] + left[6] * right[2];
				auto column0Row1 =
					left[1] * right[0] + left[4] * right[1] + left[7] * right[2];
				auto column0Row2 =
					left[2] * right[0] + left[5] * right[1] + left[8] * right[2];

				auto column1Row0 =
					left[0] * right[3] + left[3] * right[4] + left[6] * right[5];
				auto column1Row1 =
					left[1] * right[3] + left[4] * right[4] + left[7] * right[5];
				auto column1Row2 =
					left[2] * right[3] + left[5] * right[4] + left[8] * right[5];

				auto column2Row0 =
					left[0] * right[6] + left[3] * right[7] + left[6] * right[8];
				auto column2Row1 =
					left[1] * right[6] + left[4] * right[7] + left[7] * right[8];
				auto column2Row2 =
					left[2] * right[6] + left[5] * right[7] + left[8] * right[8];
				Matrix3 result;
				result[0] = column0Row0;
				result[1] = column0Row1;
				result[2] = column0Row2;
				result[3] = column1Row0;
				result[4] = column1Row1;
				result[5] = column1Row2;
				result[6] = column2Row0;
				result[7] = column2Row1;
				result[8] = column2Row2;
				return result;
			};

			/**
			 * Computes the sum of two matrices.
			 *
			 * @param {Matrix3} left The first matrix.
			 * @param {Matrix3} right The second matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 add(Matrix3& left, Matrix3& right) {
				Matrix3 result;

				result[0] = left[0] + right[0];
				result[1] = left[1] + right[1];
				result[2] = left[2] + right[2];
				result[3] = left[3] + right[3];
				result[4] = left[4] + right[4];
				result[5] = left[5] + right[5];
				result[6] = left[6] + right[6];
				result[7] = left[7] + right[7];
				result[8] = left[8] + right[8];
				return result;
			};

			/**
			 * Computes the difference of two matrices.
			 *
			 * @param {Matrix3} left The first matrix.
			 * @param {Matrix3} right The second matrix.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 subtract(Matrix3& left, Matrix3& right) {
				Matrix3 result;
				result[0] = left[0] - right[0];
				result[1] = left[1] - right[1];
				result[2] = left[2] - right[2];
				result[3] = left[3] - right[3];
				result[4] = left[4] - right[4];
				result[5] = left[5] - right[5];
				result[6] = left[6] - right[6];
				result[7] = left[7] - right[7];
				result[8] = left[8] - right[8];
				return result;
			};

			/**
			 * Computes the product of a matrix and a column vector.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @param {Cartesian3} cartesian The column.
			 * @param {Cartesian3} result The object onto which to store the result.
			 * @returns {Cartesian3} The modified result parameter.
			 */
			static Cartesian3 multiplyByVector(Matrix3& matrix, Cartesian3 cartesian) {
				auto vX = cartesian.x;
				auto vY = cartesian.y;
				auto vZ = cartesian.z;

				auto x = matrix[0] * vX + matrix[3] * vY + matrix[6] * vZ;
				auto y = matrix[1] * vX + matrix[4] * vY + matrix[7] * vZ;
				auto z = matrix[2] * vX + matrix[5] * vY + matrix[8] * vZ;
				Cartesian3 result;
				result.x = x;
				result.y = y;
				result.z = z;
				return result;
			};

			/**
			 * Computes the product of a matrix and a scalar.
			 *
			 * @param {Matrix3} matrix The matrix.
			 * @param {number} scalar The number to multiply by.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 multiplyByScalar(Matrix3& matrix, double scalar) {
				Matrix3 result;
				result[0] = matrix[0] * scalar;
				result[1] = matrix[1] * scalar;
				result[2] = matrix[2] * scalar;
				result[3] = matrix[3] * scalar;
				result[4] = matrix[4] * scalar;
				result[5] = matrix[5] * scalar;
				result[6] = matrix[6] * scalar;
				result[7] = matrix[7] * scalar;
				result[8] = matrix[8] * scalar;
				return result;
			};

			/**
			 * Computes the product of a matrix times a (non-uniform) scale, as if the scale were a scale matrix.
			 *
			 * @param {Matrix3} matrix The matrix on the left-hand side.
			 * @param {Cartesian3} scale The non-uniform scale on the right-hand side.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 *
			 * @example
			 * // Instead of Cesium.Matrix3::multiply(m, Cesium.static Matrix3 fromScale(scale), m);
			 * Cesium.Matrix3::multiplyByScale(m, scale, m);
			 *
			 * @see Matrix3::multiplyByUniformScale
			 * @see static Matrix3 fromScale
			 * @see static Matrix3 fromUniformScale
			 * @see Matrix3::setScale
			 * @see Matrix3::setUniformScale
			 * @see Matrix3::getScale
			 */
			static Matrix3 multiplyByScale(Matrix3& matrix, Cartesian3 scale) {

				Matrix3 result;
				result[0] = matrix[0] * scale.x;
				result[1] = matrix[1] * scale.x;
				result[2] = matrix[2] * scale.x;
				result[3] = matrix[3] * scale.y;
				result[4] = matrix[4] * scale.y;
				result[5] = matrix[5] * scale.y;
				result[6] = matrix[6] * scale.z;
				result[7] = matrix[7] * scale.z;
				result[8] = matrix[8] * scale.z;

				return result;
			};

			/**
			 * Computes the product of a matrix times a uniform scale, as if the scale were a scale matrix.
			 *
			 * @param {Matrix3} matrix The matrix on the left-hand side.
			 * @param {number} scale The uniform scale on the right-hand side.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @example
			 * // Instead of Cesium.Matrix3::multiply(m, Cesium.static Matrix3 fromUniformScale(scale), m);
			 * Cesium.Matrix3::multiplyByUniformScale(m, scale, m);
			 *
			 * @see Matrix3::multiplyByScale
			 * @see static Matrix3 fromScale
			 * @see static Matrix3 fromUniformScale
			 * @see Matrix3::setScale
			 * @see Matrix3::setUniformScale
			 * @see Matrix3::getScale
			 */
			static Matrix3 multiplyByUniformScale(Matrix3& matrix, double scale) {
				Matrix3 result;

				result[0] = matrix[0] * scale;
				result[1] = matrix[1] * scale;
				result[2] = matrix[2] * scale;
				result[3] = matrix[3] * scale;
				result[4] = matrix[4] * scale;
				result[5] = matrix[5] * scale;
				result[6] = matrix[6] * scale;
				result[7] = matrix[7] * scale;
				result[8] = matrix[8] * scale;

				return result;
			};

			/**
			 * Creates a negated copy of the provided matrix.
			 *
			 * @param {Matrix3} matrix The matrix to negate.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 negate(Matrix3& matrix) {
				Matrix3 result;

				result[0] = -matrix[0];
				result[1] = -matrix[1];
				result[2] = -matrix[2];
				result[3] = -matrix[3];
				result[4] = -matrix[4];
				result[5] = -matrix[5];
				result[6] = -matrix[6];
				result[7] = -matrix[7];
				result[8] = -matrix[8];
				return result;
			};

			/**
			 * Computes the transpose of the provided matrix.
			 *
			 * @param {Matrix3} matrix The matrix to transpose.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 transpose(Matrix3& matrix) {
				Matrix3 result;

				auto column0Row0 = matrix[0];
				auto column0Row1 = matrix[3];
				auto column0Row2 = matrix[6];
				auto column1Row0 = matrix[1];
				auto column1Row1 = matrix[4];
				auto column1Row2 = matrix[7];
				auto column2Row0 = matrix[2];
				auto column2Row1 = matrix[5];
				auto column2Row2 = matrix[8];

				result[0] = column0Row0;
				result[1] = column0Row1;
				result[2] = column0Row2;
				result[3] = column1Row0;
				result[4] = column1Row1;
				result[5] = column1Row2;
				result[6] = column2Row0;
				result[7] = column2Row1;
				result[8] = column2Row2;
				return result;
			};

			static double computeFrobeniusNorm(Matrix3& matrix) {
				auto norm = 0.0;
				for (auto i = 0; i < 9; ++i) {
					auto temp = matrix[i];
					norm += temp * temp;
				}

				return sqrt(norm);
			}

			const static IntVector rowVal;
			const static IntVector colVal;

			static double offDiagonalFrobeniusNorm(Matrix3& matrix) {
				// Computes the "off-diagonal" Frobenius norm.
				// Assumes matrix is symmetric.

				auto norm = 0.0;
				for (auto i = 0; i < 3; ++i) {
					auto temp = matrix[Matrix3::getElementIndex(colVal[i], rowVal[i])];
					norm += 2.0 * temp * temp;
				}

				return sqrt(norm);
			}

			static Matrix3 shurDecomposition(Matrix3& matrix) {
				// This routine was created based upon Matrix Computations, 3rd ed., by Golub and Van Loan,
				// section 8.4.2 The 2by2 Symmetric Schur Decomposition.
				//
				// The routine takes a matrix, which is assumed to be symmetric, and
				// finds the largest off-diagonal term, and then creates
				// a matrix (result) which can be used to help reduce it

				auto tolerance = CesiumMath::EPSILON15;

				auto maxDiagonal = 0.0;
				auto rotAxis = 1;

				// find pivot (rotAxis) based on max diagonal of matrix
				for (auto i = 0; i < 3; ++i) {
					auto temp = std::abs(
						matrix[Matrix3::getElementIndex(colVal[i], rowVal[i])]
					);
					if (temp > maxDiagonal) {
						rotAxis = i;
						maxDiagonal = temp;
					}
				}

				auto c = 1.0;
				auto s = 0.0;

				auto p = rowVal[rotAxis];
				auto q = colVal[rotAxis];

				if (std::abs(matrix[Matrix3::getElementIndex(q, p)]) > tolerance) {
					auto qq = matrix[Matrix3::getElementIndex(q, q)];
					auto pp = matrix[Matrix3::getElementIndex(p, p)];
					auto qp = matrix[Matrix3::getElementIndex(q, p)];

					auto tau = (qq - pp) / 2.0 / qp;
					double t;

					if (tau < 0.0) {
						t = -1.0 / (-tau + sqrt(1.0 + tau * tau));
					}
					else {
						t = 1.0 / (tau + sqrt(1.0 + tau * tau));
					}

					c = 1.0 / sqrt(1.0 + t * t);
					s = t * c;
				}

				Matrix3 result = Matrix3::IDENTITY;

				result[Matrix3::getElementIndex(p, p)] = result[
					Matrix3::getElementIndex(q, q)
				] = c;
				result[Matrix3::getElementIndex(q, p)] = s;
				result[Matrix3::getElementIndex(p, q)] = -s;

				return result;
			}

			/**
			 * Computes the eigenvectors and eigenvalues of a symmetric matrix.
			 * <p>
			 * Returns a diagonal matrix and unitary matrix such that:
			 * <code>matrix = unitary matrix * diagonal matrix * transpose(unitary matrix)</code>
			 * </p>
			 * <p>
			 * The values along the diagonal of the diagonal matrix are the eigenvalues. The columns
			 * of the unitary matrix are the corresponding eigenvectors.
			 * </p>
			 *
			 * @param {Matrix3} matrix The matrix to decompose into diagonal and unitary matrix. Expected to be symmetric.
			 * @param {object} [result] An object with unitary and diagonal properties which are matrices onto which to store the result.
			 * @returns {object} An object with unitary and diagonal properties which are the unitary and diagonal matrices, respectively.
			 *
			 * @example
			 * const a = //... symetric matrix
			 * const result = {
			 *     unitary : new Cesium.Matrix3(),
			 *     diagonal : new Cesium.Matrix3()
			 * };
			 * Cesium.Matrix3::computeEigenDecomposition(a );
			 *
			 * const unitaryTranspose = Cesium.Matrix3::transpose(result.unitary, new Cesium.Matrix3());
			 * const b = Cesium.Matrix3::multiply(result.unitary, result.diagonal, new Cesium.Matrix3());
			 * Cesium.Matrix3::multiply(b, unitaryTranspose, b); // b is now equal to a
			 *
			 * const lambda = Cesium.Matrix3::getColumn(result.diagonal, 0, new Cesium.Cartesian3()).x;  // first eigenvalue
			 * const v = Cesium.Matrix3::getColumn(result.unitary, 0, new Cesium.Cartesian3());          // first eigenvector
			 * const c = Cesium.Cartesian3::multiplyByScalar(v, lambda, new Cesium.Cartesian3());        // equal to Cesium.Matrix3::multiplyByVector(a, v)
			 */
			static std::vector<Matrix3> computeEigenDecomposition(Matrix3& matrix) {
				auto tolerance = CesiumMath::EPSILON20;
				auto maxSweeps = 10;

				auto count = 0;
				auto sweep = 0;

				std::vector<Matrix3> result{ Matrix3::IDENTITY ,Matrix3::IDENTITY };

				auto unitaryMatrix = Matrix3::IDENTITY;

				auto diagMatrix = matrix;

				auto epsilon = tolerance * computeFrobeniusNorm(diagMatrix);

				while (sweep < maxSweeps && offDiagonalFrobeniusNorm(diagMatrix) > epsilon) {
					auto jMatrix = shurDecomposition(diagMatrix);
					auto jMatrixTranspose = Matrix3::transpose(jMatrix);
					diagMatrix = Matrix3::multiply(diagMatrix, jMatrix);
					diagMatrix = Matrix3::multiply(jMatrixTranspose, diagMatrix);
					unitaryMatrix = Matrix3::multiply(unitaryMatrix, jMatrix);

					if (++count > 2) {
						++sweep;
						count = 0;
					}
				}

				return { unitaryMatrix,unitaryMatrix };
			};

			/**
			 * Computes a matrix, which contains the absolute (unsigned) values of the provided matrix's elements.
			 *
			 * @param {Matrix3} matrix The matrix with signed elements.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 abs(Matrix3& matrix) {
				Matrix3 result;
				result[0] = std::abs(matrix[0]);
				result[1] = std::abs(matrix[1]);
				result[2] = std::abs(matrix[2]);
				result[3] = std::abs(matrix[3]);
				result[4] = std::abs(matrix[4]);
				result[5] = std::abs(matrix[5]);
				result[6] = std::abs(matrix[6]);
				result[7] = std::abs(matrix[7]);
				result[8] = std::abs(matrix[8]);

				return result;
			};

			/**
			 * Computes the determinant of the provided matrix.
			 *
			 * @param {Matrix3} matrix The matrix to use.
			 * @returns {number} The value of the determinant of the matrix.
			 */
			static double determinant(Matrix3& matrix) {
				auto m11 = matrix[0];
				auto m21 = matrix[3];
				auto m31 = matrix[6];
				auto m12 = matrix[1];
				auto m22 = matrix[4];
				auto m32 = matrix[7];
				auto m13 = matrix[2];
				auto m23 = matrix[5];
				auto m33 = matrix[8];

				return (
					m11 * (m22 * m33 - m23 * m32) +
					m12 * (m23 * m31 - m21 * m33) +
					m13 * (m21 * m32 - m22 * m31)
					);
			};

			/**
			 * Computes the inverse of the provided matrix.
			 *
			 * @param {Matrix3} matrix The matrix to invert.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 *
			 * @exception {DeveloperError} matrix is not invertible.
			 */
			static Matrix3 inverse(Matrix3 matrix) {

				auto m11 = matrix[0];
				auto m21 = matrix[1];
				auto m31 = matrix[2];
				auto m12 = matrix[3];
				auto m22 = matrix[4];
				auto m32 = matrix[5];
				auto m13 = matrix[6];
				auto m23 = matrix[7];
				auto m33 = matrix[8];
				auto determinant = Matrix3::determinant(matrix);

				Matrix3 result;
				result[0] = m22 * m33 - m23 * m32;
				result[1] = m23 * m31 - m21 * m33;
				result[2] = m21 * m32 - m22 * m31;
				result[3] = m13 * m32 - m12 * m33;
				result[4] = m11 * m33 - m13 * m31;
				result[5] = m12 * m31 - m11 * m32;
				result[6] = m12 * m23 - m13 * m22;
				result[7] = m13 * m21 - m11 * m23;
				result[8] = m11 * m22 - m12 * m21;

				auto scale = 1.0 / determinant;
				return Matrix3::multiplyByScalar(result, scale);
			};

			/**
			 * Computes the inverse transpose of a matrix.
			 *
			 * @param {Matrix3} matrix The matrix to transpose and invert.
			 * @param {Matrix3} result The object onto which to store the result.
			 * @returns {Matrix3} The modified result parameter.
			 */
			static Matrix3 inverseTranspose(Matrix3 matrix) {
				return Matrix3::inverse(Matrix3::transpose(matrix));
			};

			/**
			 * Compares the provided matrices componentwise and returns
			 * <code>true</code> if they are equal, <code>false</code> otherwise.
			 *
			 * @param {Matrix3} [left] The first matrix.
			 * @param {Matrix3} [right] The second matrix.
			 * @returns {boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
			 */
			static bool equals(Matrix3& left, Matrix3& right) {
				return (
					left[0] == right[0] &&
					left[1] == right[1] &&
					left[2] == right[2] &&
					left[3] == right[3] &&
					left[4] == right[4] &&
					left[5] == right[5] &&
					left[6] == right[6] &&
					left[7] == right[7] &&
					left[8] == right[8]
					);
			};

			/**
			 * Compares the provided matrices componentwise and returns
			 * <code>true</code> if they are within the provided epsilon,
			 * <code>false</code> otherwise.
			 *
			 * @param {Matrix3} [left] The first matrix.
			 * @param {Matrix3} [right] The second matrix.
			 * @param {number} [epsilon=0] The epsilon to use for equality testing.
			 * @returns {boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
			 */
			static bool equalsEpsilon(Matrix3 left, Matrix3 right, double epsilon = 0.0) {

				return (
					std::abs(left[0] - right[0]) <= epsilon &&
					std::abs(left[1] - right[1]) <= epsilon &&
					std::abs(left[2] - right[2]) <= epsilon &&
					std::abs(left[3] - right[3]) <= epsilon &&
					std::abs(left[4] - right[4]) <= epsilon &&
					std::abs(left[5] - right[5]) <= epsilon &&
					std::abs(left[6] - right[6]) <= epsilon &&
					std::abs(left[7] - right[7]) <= epsilon &&
					std::abs(left[8] - right[8]) <= epsilon
					);
			};

			/**
			 * An immutable Matrix3 instance initialized to the identity matrix.
			 *
			 * @type {Matrix3}
			 * @constant
			 */
			const static Matrix3 IDENTITY;

			/**
			 * An immutable Matrix3 instance initialized to the zero matrix.
			 *
			 * @type {Matrix3}
			 * @constant
			 */
			const static Matrix3 ZERO;

			/**
			 * The index into Matrix3 for column 0, row 0.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN0ROW0 = 0;

			/**
			 * The index into Matrix3 for column 0, row 1.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN0ROW1 = 1;

			/**
			 * The index into Matrix3 for column 0, row 2.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN0ROW2 = 2;

			/**
			 * The index into Matrix3 for column 1, row 0.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN1ROW0 = 3;

			/**
			 * The index into Matrix3 for column 1, row 1.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN1ROW1 = 4;

			/**
			 * The index into Matrix3 for column 1, row 2.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN1ROW2 = 5;

			/**
			 * The index into Matrix3 for column 2, row 0.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN2ROW0 = 6;

			/**
			 * The index into Matrix3 for column 2, row 1.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN2ROW1 = 7;

			/**
			 * The index into Matrix3 for column 2, row 2.
			 *
			 * @type {number}
			 * @constant
			 */
			const static int COLUMN2ROW2 = 8;

			int length()
			{
				return Matrix3::packedLength;
			}

			/**
			 * Compares this matrix to the provided matrix componentwise and returns
			 * <code>true</code> if they are equal, <code>false</code> otherwise.
			 *
			 * @param {Matrix3} [right] The right hand side matrix.
			 * @returns {boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
			 */
			bool equals(Matrix3& right) {
				return Matrix3::equals(*this, right);
			};

			/**
			 * @private
			 */
			bool equalsArray(Matrix3& matrix, DoubleVector array1, size_t offset) {
				return (
					matrix[0] == array1[offset] &&
					matrix[1] == array1[offset + 1] &&
					matrix[2] == array1[offset + 2] &&
					matrix[3] == array1[offset + 3] &&
					matrix[4] == array1[offset + 4] &&
					matrix[5] == array1[offset + 5] &&
					matrix[6] == array1[offset + 6] &&
					matrix[7] == array1[offset + 7] &&
					matrix[8] == array1[offset + 8]
					);
			};

			/**
			 * Compares this matrix to the provided matrix componentwise and returns
			 * <code>true</code> if they are within the provided epsilon,
			 * <code>false</code> otherwise.
			 *
			 * @param {Matrix3} [right] The right hand side matrix.
			 * @param {number} [epsilon=0] The epsilon to use for equality testing.
			 * @returns {boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise.
			 */
			bool equalsEpsilon(Matrix3 right, double epsilon) {
				return Matrix3::equalsEpsilon(*this, right, epsilon);
			};

			/**
			 * Creates a string representing this Matrix with each row being
			 * on a separate line and in the format '(column0, column1, column2)'.
			 *
			 * @returns {string} A string representing the provided Matrix with each row being on a separate line and in the format '(column0, column1, column2)'.
			 */
			String toString() {
				return str_format(
					"%d,%d,%d,%d,%d,%d,%d,%d,%d",
					this->data[0], this->data[3], this->data[6],
					this->data[1], this->data[4], this->data[7],
					this->data[2], this->data[5], this->data[8]
				);
			}
		};
	}
}
#endif

